package ms.unicorn.dsl.impl;

import java.util.HashMap;
import java.util.Map;

import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EDataType;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EcorePackage;

import org.eclipse.emf.edit.provider.ItemPropertyDescriptor;

import ms.unicorn.EPhantomAttribute;
import ms.unicorn.UnicornPackage;
import ms.unicorn.dsl.IElementField;
import ms.unicorn.dsl.ILanguageElement;
import ms.unicorn.dsl.IModelLanguage;
import ms.unicorn.dsl.runtime.ExecutionContext;
import ms.unicorn.dsl.runtime.VariableStorage;
import ms.unicorn.dsl.unicornDSL.ElementField;
import ms.unicorn.dsl.unicornDSL.NamedProperty;
import ms.unicorn.dsl.unicornDSL.XExpression;
import ms.unicorn.dsl.unicornDSL.impl.ElementFieldImpl;

public class LanguageElementField extends ElementFieldImpl implements
		IElementField {
	
	public static EDataType INT;
	public static EDataType STRING;
	public static EDataType BOOLEAN;
	public static EDataType FLOAT;
	public static EDataType DATE;

	private static Map<String, EAttribute> builtinFields;
	private static Map<String, EDataType> primitiveType;
	private static Map<EDataType, Object> propertyImages;
	private static EcorePackage ecorePackage;
	
	static {
		ecorePackage = (EcorePackage)EPackage.Registry.INSTANCE.getEPackage(EcorePackage.eNS_URI);
		
		builtinFields = new HashMap<String, EAttribute>(5);
		builtinFields.put("NAME", UnicornPackage.eINSTANCE.getElement_Name());
		builtinFields.put("TEXT", UnicornPackage.eINSTANCE.getElement_Text());
		builtinFields.put("DESCRIPTION", UnicornPackage.eINSTANCE.getElement_Description());
		builtinFields.put("STEREOTYPE", UnicornPackage.eINSTANCE.getElement_Stereotype());
		
		primitiveType = new HashMap<String, EDataType>();
		primitiveType.put("int", INT = ecorePackage.getEInt());
		primitiveType.put("string", STRING = ecorePackage.getEString());
		primitiveType.put("date", DATE = ecorePackage.getEDate());
		primitiveType.put("float", FLOAT = ecorePackage.getEFloat());
		primitiveType.put("boolean", BOOLEAN = ecorePackage.getEBoolean());
		
		propertyImages = new HashMap<EDataType, Object>();
		propertyImages.put(ecorePackage.getEInt(), ItemPropertyDescriptor.INTEGRAL_VALUE_IMAGE);
		propertyImages.put(ecorePackage.getEString(), ItemPropertyDescriptor.TEXT_VALUE_IMAGE);
		propertyImages.put(ecorePackage.getEBoolean(), ItemPropertyDescriptor.BOOLEAN_VALUE_IMAGE);
		propertyImages.put(ecorePackage.getEFloat(), ItemPropertyDescriptor.REAL_VALUE_IMAGE);		
	}

	private ILanguageElement owner;
	private EPhantomAttribute attr;
	private Object defaultValue = VariableStorage.UNEVALUATED;

	public ILanguageElement getOwner() {
		if (owner == null) {
			for (EObject lang = this.eContainer(); lang != null; lang = lang.eContainer())
				if (lang instanceof ILanguageElement) 
					return owner = (ILanguageElement)lang;
		}
		return owner;
	}
	
	public IModelLanguage getLanguage() {
		return getOwner().getLanguage();
	}
	
	@SuppressWarnings({ "unchecked" })
	public <T> T getElementProperty(String name, T defvalue, ExecutionContext context) {
		
		for (EObject eprop: this.getTags()) 
			if (eprop instanceof NamedProperty) {
				NamedProperty prop = (NamedProperty) eprop;
				if (prop.getName().equalsIgnoreCase(name)) {
					XExpression value = prop.getValue();
					if (value == null) return (T)Boolean.TRUE;
					return (T)getLanguage().evaluate(prop.getValue(), context);
				}
			}
		return defvalue;
	}
	
	public <T> T getElementProperty(String name, T defvalue) {
		return getElementProperty(name, defvalue, getLanguage().getContext());
	}
	
	@Override
	public EDataType getDataType() {
		return primitiveType.get(this.getType());
	}
	
	public boolean isCalculated() {
		return getElementProperty("expression", false);
	}

	@Override
	public boolean isEditable() {
		return !getElementProperty("readonly", false) && (!(getDefaultValue() instanceof XExpression));
	}

	@Override
	public boolean isVisible() {
		return getElementProperty("visible", true);
	}

	@Override
	public String getCategory() {
		return getElementProperty("category", "Misc");
	}

	@Override
	public String getDisplayName() {
		return getElementProperty("text", getName());
	}

	@Override
	public String getDescription() {
		return getElementProperty("description", "");
	}

	@Override
	public boolean isMultiLine() {
		return getElementProperty("multiline", false);
	}

	@Override
	public Object getDefaultImage() {
		Object image = propertyImages.get(getDataType());
		return image != null ? image : ItemPropertyDescriptor.GENERIC_VALUE_IMAGE;
	}

	@Override
	public EPhantomAttribute getAttribute() {
		if (attr == null) {
			Class<?> clazz = getOwner().getElementClass();
			EClass eClass = getOwner().getElementEClass();
			attr = new EPhantomAttributeImpl(this, clazz, eClass);
		}
		return attr;
	}

	@Override
	public Object getDefaultValue() {
		if (defaultValue == VariableStorage.UNEVALUATED) {
			defaultValue = null;
			if (getValue() != null)
				defaultValue = isCalculated()? getValue(): getLanguage().evaluate(getValue());
		}
		return defaultValue;
	}

}
